package il.technion.cs236369.webserver.configuration;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import il.technion.cs236369.webserver.simpleServlets.ISimpleServlet;

/**
 * Implements the IServerConfiguration interface. Takes the configuration from given xml file.
 * @author Ofir Haviv and Itamar Talmi
 *
 */
public class ServerConfiguration implements IServerConfiguration {
	
	private Map<String, String> mimeTypes;
	private Map<String, ISimpleServlet> simpleServlets;
	
	/**
	 * The constructor of the ServerConfiguration class.
	 * @param configurationFileName - The configuration file to parse.
	 * @throws Exception - If any exception accrue during the file parsing,
	 * 					   or if the file dose not exist.
	 */
	public ServerConfiguration(String configurationFileName) throws Exception{
		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		docFactory.setNamespaceAware(true);
		DocumentBuilder builder = docFactory.newDocumentBuilder();
		Document doc = builder.parse(configurationFileName);

		
		
		initMimeTypes(doc);
		initSimpleServlets(doc);
	}
	
	private void initMimeTypes(Document doc) throws XPathExpressionException{
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();

		NodeList nl = (NodeList) xpath.compile("//mime/mime-mapping").evaluate(
				doc, XPathConstants.NODESET);
		mimeTypes = new HashMap<>(nl.getLength());
		
		//System.out.println(nl.getLength());

		for (int i = 0; i < nl.getLength(); ++i) {
			String extension = xpath.compile("./extension")
					.evaluate(nl.item(i));
			String mime_type = xpath.compile("./mime-type")
					.evaluate(nl.item(i));
			//System.out.println(extension + ",  " + mime_type);
			mimeTypes.put(extension, mime_type);
		}	
	}
	
	private Set<String> getAllMappingFromNode(Node node) throws XPathExpressionException{
		Set<String> res = new HashSet<String>();
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();
		NodeList mappingPaths = (NodeList) xpath.compile("./mapping").evaluate(node, XPathConstants.NODESET);
		
		for (int i = 0; i < mappingPaths.getLength(); i++)
			res.add(xpath.compile(".").evaluate(mappingPaths.item(i)));

		return res;
	}
	
	private void initSimpleServlets(Document doc) throws XPathExpressionException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException{
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();
		simpleServlets = new HashMap<>();

		NodeList servletNodes = (NodeList) xpath.compile("//simple-servlets/simple-servlet").evaluate(
				doc, XPathConstants.NODESET);
		for (int i = 0; i < servletNodes.getLength(); i++){
			Node node = servletNodes.item(i);
			NamedNodeMap nodeAttributes = node.getAttributes();
			Node classAttribute = nodeAttributes.getNamedItem("class");
			String className = classAttribute.getNodeValue();
			//System.out.println("className = " + className);
			ISimpleServlet theServlet = (ISimpleServlet)Class.forName(className).getConstructor().newInstance();
			Set<String> paths = getAllMappingFromNode(node);
			for (String path : paths)
				simpleServlets.put(path, theServlet);
		}
		
		
	}
	
	@Override
	public String getMimeType(String extension) {
		return mimeTypes.get(extension);
	}

	@Override
	public ISimpleServlet getServlet(String url) {
		return simpleServlets.get(url);
	}
}
